Crate for providing metadata for Gear programs.
Metadata is used to describe the interface of a Gear program. For example,
it can be used when uploading a program using
.
The metadata informs the user about the program's interface and allows them
to interact with it using custom types on web applications UI.
Another use case is to parse metadata in JavaScript using the `gear-js`
library and get the metadata details for some custom UI.
Note that metadata is not required for a Gear program to work. It is only
used to provide additional information about the program. Also, metadata
can be used for various purposes but we will focus on the use cases related
to the .
To generate a metadata output file for a program, you need:
- Add `gmeta` crate to your `Cargo.toml` file.
- Define an empty struct that will identify the program metadata.
- Implement the [`Metadata`] trait for this struct by defining the
associated types of the trait.
- **Option 1**: Call [`gear_wasm_builder::build_with_metadata`](https://docs.gear.rs/gear_wasm_builder/fn.build_with_metadata.html)
function in `build.rs` file.
- **Option 2**: Convert metadata to hex string using [`MetadataRepr::hex`]
function and write it to the text file.
# Examples
In this example we will create a simple ping-pong program. Let's define
message types and metadata in a separate `ping-io` crate to be able to use
it in both program and `build.rs` files.
We will define message types for `handle()` and `state()` functions.
- `ping-io` crate:
```
#[no_std]
use gmeta::{InOut, Metadata, Out};
use gstd::prelude::*;
// Message type for `handle()` function.
#[derive(Encode, Decode, TypeInfo)]
pub enum PingPong {
Ping,
Pong,
}
// Metadata struct.
pub struct ProgramMetadata;
impl Metadata for ProgramMetadata {
// The unit tuple is used as neither incoming nor outgoing messages are
// expected in the `init()` function.
type Init = ();
// We use the same `PingPong` type for both incoming and outgoing
// messages.
type Handle = InOut;
// The unit tuple is used as we don't use asynchronous interaction in this
// program.
type Others = ();
// The unit tuple is used as we don't process any replies in this program.
type Reply = ();
// The unit tuple is used as we don't process any signals in this program.
type Signal = ();
// We return a counter value (`i32`) in the `state()` function in this program.
type State = Out;
}
```
- `ping` program crate:
```
#[no_std]
use gmeta::{InOut, Metadata};
use gstd::{msg, prelude::*};
# const IGNORE: &'static str = stringify! {
use ping_io::PingPong;
# };
// Counter that will be incremented on each `Ping` message.
static mut COUNTER: i32 = 0;
# #[derive(Encode, Decode, TypeInfo)]
# pub enum PingPong {
# Ping,
# Pong,
# }
#
#[no_mangle]
extern "C" fn handle() {
// Load incoming message of `PingPong` type.
let payload: PingPong = msg::load().expect("Unable to load");
if let PingPong::Ping = payload {
unsafe { COUNTER += 1 };
// Send a reply message of `PingPong` type back to the sender.
msg::reply(PingPong::Pong, 0).expect("Unable to reply");
}
}
#[no_mangle]
extern "C" fn state() {
msg::reply(unsafe { COUNTER }, 0).expect("Unable to reply");
}
```
- `build.rs` file:
```no_run
# const IGNORE: &'static str = stringify! {
use ping_io::ProgramMetadata;
# };
#
# pub struct ProgramMetadata;
# impl gmeta::Metadata for ProgramMetadata {
# type Init = ();
# type Handle = ();
# type Others = ();
# type Reply = ();
# type Signal = ();
# type State = ();
# }
fn main() {
gear_wasm_builder::build_with_metadata::();
}
```
You can also generate metadata manually and write it to the file without
using `build.rs`:
```
use gmeta::{Metadata, Out};
# const IGNORE: &'static str = stringify! {
use ping_io::ProgramMetadata;
# };
use std::fs;
# #[derive(gstd::Encode, gstd::Decode, gstd::TypeInfo)]
# pub enum PingPong {
# Ping,
# Pong,
# }
#
# pub struct ProgramMetadata;
# impl gmeta::Metadata for ProgramMetadata {
# type Init = ();
# type Handle = (PingPong, PingPong);
# type Others = ();
# type Reply = ();
# type Signal = ();
# type State = Out;
# }
#
let metadata_hex = ProgramMetadata::repr().hex();
assert_eq!(metadata_hex.len(), 146);
fs::write("ping.meta.txt", metadata_hex).expect("Unable to write");
```
You can parse generated metadata file using `gear-js` API in JavaScript:
```javascript
import { getProgramMetadata } from '@gear-js/api';
import { readFileSync } from 'fs';
const metadataHex = readFileSync('ping.meta.txt', 'utf-8');
const metadata = getProgramMetadata('0x' + metadataHex);
console.log('Registry:', metadata.regTypes);
console.log('Types:', metadata.types);
```
This will print the following:
```text
Registry: Map(2) {
0 => { name: 'RustOutPingPong', def: '{"_enum":["Ping","Pong"]}' },
1 => { name: 'i32', def: null }
}
Types: {
init: { input: null, output: null },
handle: { input: 0, output: 0 },
reply: { input: null, output: null },
others: { input: null, output: null },
signal: null,
state: 1
}
```